package com.srp.common.utils;


import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.beans.BeanCopier;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class Dto2Entity {
    private static Logger logger = LoggerFactory.getLogger(Dto2Entity.class);
    // 使用多线程安全的Map来缓存BeanCopier，由于读操作远大于写，所以性能影响可以忽略
    public static ConcurrentHashMap<String, BeanCopier> beanCopierMap = new ConcurrentHashMap<String, BeanCopier>();

    /**
     * 通过cglib BeanCopier形式
     * @param source
     * @param target
     */
    public static void copyProperties(Object source, Object target) {
        String beanKey = generateKey(source.getClass(), target.getClass());
        BeanCopier copier = null;
        copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        beanCopierMap.putIfAbsent(beanKey, copier);// putIfAbsent已经实现原子操作了。
        copier = beanCopierMap.get(beanKey);
        copier.copy(source, target, null);
    }

    private static String generateKey(Class<?> class1, Class<?> class2) {
        return class1.toString() + class2.toString();
    }

    /**
     ×通过常规反射形式
     * DTO对象转换为实体对象。如命名不规范或其他原因导致失败。
     * @param t 源转换的对象
     * @param e 目标转换的对象
     *
     */
    public static <T,E> void translate(T t,E e){
        Method[] tms=t.getClass().getDeclaredMethods();
        Method[] tes=e.getClass().getDeclaredMethods();
        for(Method m1:tms){
            if(m1.getName().startsWith("get")){
                String mNameSubfix=m1.getName().substring(3);
                String forName="set"+mNameSubfix;
                for(Method m2:tes){
                    if(m2.getName().equals(forName)){
                        // 如果类型一致，或者m2的参数类型是m1的返回类型的父类或接口
                        boolean canContinue = m2.getParameterTypes()[0].isAssignableFrom(m1.getReturnType());
                        if (canContinue) {
                            try {
                                m2.invoke(e, m1.invoke(t));
                                break;
                            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
                                // TODO Auto-generated catch block

                                logger.debug("DTO 2 Entity转换失败");
                                e1.printStackTrace();
                            }
                        }
                    }
                }
            }

        }
        logger.debug("转换完成");

    }

    /**
     × 两个不同的对象属性合并到一个对象
     * DTO对象转换为实体对象。如命名不规范或其他原因导致失败。
     * @param t1 源转换的对象1
     * @param t2 源转换的对象2,如果字段与1重复，则覆盖
     * @param e 目标转换的对象
     *
     */
    public static <T1,T2,E> void translate(T1 t1,T2 t2,E e){
        ConcurrentHashMap<String, Method> methodSafeMap1 = new ConcurrentHashMap<>();
        ConcurrentHashMap<String, Method> methodSafeMap2 = new ConcurrentHashMap<>();
        Method[] tms1=t1.getClass().getDeclaredMethods();
        Method[] tms2=t2.getClass().getDeclaredMethods();
        for(Method m1:tms1){
            if(m1.getName().startsWith("get")){
                String mNameSubfix=m1.getName().substring(3);
                String forName="set"+mNameSubfix;
                methodSafeMap1.putIfAbsent(forName,m1);
            }
        }
        for(Method m2:tms2){
            if(m2.getName().startsWith("get")){
                String mNameSubfix=m2.getName().substring(3);
                String forName="set"+mNameSubfix;
                methodSafeMap2.putIfAbsent(forName,m2);
            }
        }
        Method m3;
        for (Map.Entry<String,Method> entry : methodSafeMap1.entrySet()){
           try{
               m3 = e.getClass().getDeclaredMethod(entry.getKey(),entry.getValue().getReturnType());
               m3.invoke(e,entry.getValue().invoke(t1));
           }catch (Exception exp){
               exp.printStackTrace();
           }
        }
        for (Map.Entry<String,Method> entry : methodSafeMap2.entrySet()){
            try{
                m3 = e.getClass().getDeclaredMethod(entry.getKey(),entry.getValue().getReturnType());
                m3.invoke(e,entry.getValue().invoke(t2));
            }catch (Exception exp){
                exp.printStackTrace();
            }
        }
        logger.debug("合并完成");
    }

  }
